package org.example;

import org.entity.Author;
import org.entity.Book;

import java.util.*;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.stream.Stream;

/**
 * Created by Lenovo
 * DATA:2024/6/22
 **/

/**
 * 创建流的方式：
 *
 *
 * 单列集合：集合对象.stream()
 * List<Book> books=new ArrayList();
 * Stream<Book> stream=books.stream();
 *
 * 数组：Arrays.stream(array) 或者使用 Stream.of(数组) 来创建
 * Integer[] arr={1,2,3,4,5};
 * Stream<Integer> stream=Arrays.stream(arr);
 * Stream<Integer> stream=Stream.of(arr);
 *
 * 双列集合：转换成单列集合之后在创建
 * Map<String,Integer> map =new HashMap<>();
 * Stream<String> stream=map.keySet().stream();//只拿到key的集合
 * Stream<Integer> stream=map.values().stream();//只拿到value的集合
 * Stream<Map.Entry<String,Integer>> stream=map.entrySet().stream();
 *
 * 多列集合：集合对象.stream().flatMap(Collection::stream)
 * filter 可以对流中的元素进行条件过滤，符合过滤条件的才能继续留在流中
 * map 可以把流中的元素进行计算或者类型转换
 * distinct 对元素进行去重，注意distinct方法是依赖Object中的equals方法来判断是否是相等的，所以需要注意重写equals方法。
 * sorted 可以对流中的元素进行排序。
 * 注意如果调用的是空参的sorted()方法，需要流中的元素实现了Comparable接口(并重写一个方法)
 * limit 可以设置流的最大长度，超出的部分将被抛弃，
 * skip 可以跳过前n个元素，返回剩下的元素。
 *
 * flatMap map只能将一个对象转换为另一个对象来作为流中的元素。而flatMap可以把一个对象转换成多个对象作为流中的元素。
 *(比如我们获取每个对象身上的book集合，使用flatMap 可以把book集合中的所有对象全部拿出来)
 * 
 * 
 * 终结操作
 * forEach 对流中的元素进行遍历操作，我们通过传入的参数去指定对遍历到的元素进行什么具体操作
 * count 获取当前流中的元素的个数
 * max 获取当前流中的元素的最大值
 * min 获取当前流中的元素的最小值
 * collect 把当前流转换为一个集合
 * 终结操作查找与匹配
 * anyMatch 可以用来判断是否有任意符合匹配条件的元素，结果为boolean 类型
 * allMatch 可以用来判断是否都符合匹配条件，结果为boolean 类型，如果都符合结果为true,否则结果为false。
 * noneMatch 可以用来判断是否都不符合匹配条件，结果为boolean 类型，如果都不符合结果为true,否则结果为false。
 * findAny 获取流中的任意一个元素，该方法没有办法保证获取到的一定是流中的第一个元素。
 * findFirst 获取流中的第一个元素
 *
 * reduce归并
 * 对流中的数据按照你指定的计算方式计算成一个结果。（缩减操作）
 * reduce的作用是把stream中的元素组合起来，我们可以传入一个初始值，
 * 他会按照我们的计算方式一次拿流中的元素和初始化值进行计算，计算结果在和后面的元素计算
 *
 * reduce两个参数的重载形式的内部计算方式如下：
 * T result =identity;
 * for(T element: this.stream)
 *      result = accumulator.apply(result, element);
 * return result;
 *其中identity 就是我们可以通过方法参数传入的初始值，accumulator 的apply具体计算什么也是我们通过方法参数来确定的。
 *
 * reduce 一个参数的重载形式的内部计算
 * boolean foundAny=false;
 * T result=null;
 * for(T element: this.stream)
 *      if(foundAny)
 *          result = accumulator.apply(result, element);
 *      else
 *          result = element;
 *          foundAny = true;
 * return foundAny ? Optional.of(result) : Optional.empty();
 *
 *
 *
 * 注意事项：
 * 惰性求值（如果没有终结操作，没有中间操作是不会得到执行的）
 * 流是一次性的（一旦一个流对象经过一个终结操作之后这个流对象就不能再被使用了）
 * 不会影响元原数据（我们在流中可以多数据做很多事，但是正常情况下不回应性原来集合中的元素的，这往往也是我们期望的）
 *
 *
 *
 *
 *
 *
 */
public class StreamDemo {
    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        System.out.println("authors = " + authors);
       authors.stream()
               .distinct().filter(author -> author.getAge() < 20).forEach(author -> System.out.println("author = " + author.getName()));
        System.out.println("authors = " + authors);

        HashMap<String, Integer> stringIntegerHashMap = new HashMap<>();
        stringIntegerHashMap.put("1", 1);
        stringIntegerHashMap.put("2", 2);
        stringIntegerHashMap.put("3", 3);
        stringIntegerHashMap.put("4", 4);
        stringIntegerHashMap.put("5", 5);
        Set<String> strings = stringIntegerHashMap.keySet();

//        authors.stream().flatMap(author -> author.getBooks().stream()).distinct().forEach(book -> System.out.println("book = " + book));

        //获取元素的最大值。
        Integer integer = authors.stream().flatMap(author -> author.getBooks().stream()).map(book -> book.getScore()).distinct().max((o1, o2) -> o1 - o2).get();
        System.out.println("integer = " + integer);
        //查抄与匹配
        boolean b = authors.stream().anyMatch(author -> author.getAge() < 20);//是否有对象的年龄小于20
        System.out.println("b = " + b);
        boolean b1 = authors.stream().allMatch(author -> author.getAge() < 20);//判断是否所有对象的年龄都小于20
        System.out.println("b1 = " + b1);
        boolean b2 = authors.stream().noneMatch(author -> author.getAge() < 20);//判断是否所有对象的年龄都不小于20
        System.out.println("b2 = " + b2);


        //reduce
        //计算年龄的和。
        Integer reduce = authors.stream().distinct().map(author -> author.getAge()).reduce(0, new BinaryOperator<Integer>() {
            @Override
            public Integer apply(Integer integer, Integer integer2) {
                return integer + integer2;
            }
        });
        System.out.println("reduce = " + reduce);
        //求年年龄最大的
        Integer reduce1 = authors.stream().distinct().map(author -> author.getAge()).reduce(Integer.MIN_VALUE, new BinaryOperator<Integer>() {
            @Override
            public Integer apply(Integer integer, Integer integer2) {
                return integer > integer2 ? integer : integer2;//年龄比较
            }
        });
        System.out.println("reduce1 = " + reduce1);

        //使用一个参数的重载方法获取最小值。（其实就相当于用第一个数作为最开始舒适化的参数。）
        Optional<Integer> reduce2 = authors.stream().map(author -> author.getAge()).reduce(new BinaryOperator<Integer>() {
            @Override
            public Integer apply(Integer integer, Integer integer2) {
                return integer>integer2?integer2:integer;//年龄比较获取最小值
            }
        });
        reduce2.ifPresent(System.out::println);


        /**
         * 方法引用
         */
        //引用类的静态方法
        authors.stream().map(age->String.valueOf(age));
        authors.stream()
                .map(String::valueOf);//引用类的静态方法

        //引用对象的实例方法：
        //使用前
        Stream<Author> stream = authors.stream();
        StringBuffer sb = new StringBuffer();
        stream.map(author -> author.getName()).forEach(name->sb.append(name));

        //使用后
        Stream<Author> stream1 = authors.stream();
        StringBuffer sb1 = new StringBuffer();
        stream1.map(Author::getName).forEach(sb::append);//引用对象的实例方法


        System.out.println("=================并行流=================");
        Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
//        integerStream.forEach(i -> System.out.println(i+Thread.currentThread().getName()));//串行流
        integerStream.parallel().forEach(i -> System.out.println(i+Thread.currentThread().getName()));//并行流


        System.out.println(" ==================peek()帮助我们进行调试================ ");
        /**
         * peek可以放在stream链的任何位置获取当前文职的数据信息，方便我们进行排查错误和调试。
         */
        Stream<Integer> integerStream1 = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        integerStream1.map(value->value+10).peek(value->System.out.println("peek:"+value)).filter(value->value>15).forEach(value->System.out.println(value));



    }
    private static List<Author> getAuthors(){
        Author author = new Author(1L, "zhangsan", 15, "hello", null);
        Author author1 = new Author(2L, "lisi", 23, "hello", null);
        Author author2 = new Author(3L, "wangwu", 23, "hello", null);
        Author author3 = new Author(4L, "zhaoliu", 23, "hello", null);
        Author author4 = new Author(5L, "xiaohuang", 23, "hello", null);
        Author author5 = new Author(1L, "zhangsan", 15, "hello", null);;

        List<Book> books1=new ArrayList<>();
        List<Book> books2=new ArrayList<>();
        List<Book> books3=new ArrayList<>();
        books1.add(new Book(1L, "book1", "哲学,爱情", 88, "intro1"));
        books1.add(new Book(2L, "book2", "个人成长，爱情", 99, "intro2"));
        books2.add(new Book(3L, "book3", "哲学", 85, "intro3"));
        books2.add(new Book(3L, "book3", "哲学", 85, "intro4"));
        books2.add(new Book(3L, "book3", "爱情，个人传记", 56, "intro5"));
        books3.add(new Book(5L, "book5", "爱情", 56, "intro6"));
        books3.add(new Book(6L, "book6", "个人传记", 100, "intro7"));
        books3.add(new Book(6L, "book6", "个人传记", 100, "intro8"));

        author.setBooks(books1);
        author2.setBooks(books2);
        author3.setBooks(books3);
        author4.setBooks(books2);
        author5.setBooks(books1);
        List<Author> authors=new ArrayList<>(Arrays.asList(author,author2,author3,author4,author5));
        return authors;
    }
}
